﻿using System;
using System.Collections.Generic;
using System.Linq;
using Godot;

namespace DragDemo.DragablePanels;

public partial class DragableHorizontalStackPanel : DragablePanelBase
{
  #region prop

  /// <summary>
  /// 所有子节点列表
  /// </summary>
  private List<Control> _children = new List<Control>();

  /// <summary>
  /// 正在动画中的index集合(动画中不参与逻辑)
  /// </summary>
  private List<int> _moveControlIndexs = new List<int>();

  /// <summary>
  /// 父节点 
  /// </summary>
  private Control _parent;

  /// <summary>
  /// 当前被移动的control的初始pos(用于交换时使用坐标)
  /// </summary>
  private Vector2 _currentDragControlPos;
  
  /// <summary>
  /// 补齐control和鼠标的距离
  /// </summary>
  private Vector2 _offset;
  
  /// <summary>
  /// 当前拖动的节点index
  /// </summary>
  private int _currentDragControlIndex = -1;
  
  /// <summary>
  /// 横向间隔大小
  /// </summary>
  [Export]
  private double IntervalSpace = 10;

  #endregion

  #region ctor

  public DragableHorizontalStackPanel()
  {
    GD.Print("节点进入树");
    // 节点进入树时执行
    TreeEntered += DragableVerticalStackPanel_TreeEntered;
  }

  #endregion

  #region overrides

  public override void _Ready()
  {
    base._Ready();
    Arrange();
  }

  public override void _Process(double delta)
  {
    base._Process(delta);

    // 刚按下时。
    if (Input.IsActionJustPressed(_actionName))
    {
      DragStart();
    }
    // 拖动时。
    else if (Input.IsActionPressed(_actionName))
    {
      DragMove();
    }
    // 松开时
    else if (Input.IsActionJustReleased(_actionName))
    {
      Arrange();
    }
  }

  /// <summary>
  /// 重新定位所有子节点
  /// </summary>
  public override void Arrange()
  {
    var sum = -IntervalSpace;
    // 行数
    var posY = _parent.Position.Y;
    foreach (var child in _children)
    {
      var wight = child.Size.X;
      var x = IntervalSpace + sum;
      // 如果超出了空控件的横向范围 换行
      if (x + wight > _parent.Position.X + _parent.Size.X)
      {
        // 重置sum
        sum = -IntervalSpace;
        // 重置x
        x = IntervalSpace + sum;
        
        // Y换行
        posY += child.Size.Y + (float)IntervalSpace;
      }
      
      child.Position = new Vector2((float)x, posY);
      sum = x + wight;
    }
  }

  #endregion

  #region darg

  private void DragStart()
  {
    // 获取鼠标在窗口中的坐标
    var mousePos = GetViewport().GetViewport().GetMousePosition();
    var hitResult = HitTest(mousePos);
    if (hitResult < 0) return;

    // 设置当前拖拽的内容。
    _currentDragControlIndex = hitResult;

    _currentDragControlPos = _children[_currentDragControlIndex].Position;
    _offset = _children[_currentDragControlIndex].Position - mousePos;
    _children[_currentDragControlIndex].Position = mousePos + _offset;

    // _childrenIgnoreDragObject = _children.Where(c => c != hitResult).ToList();

    //_lastInsertIndex = GetInsertIndex(_parent, _childrenIgnoreDragObject, hitResult, mousePos.Y, IntervalSpace);
  }

  private void DragMove()
  {
    if (_currentDragControlIndex < 0) return;
    
    var mousePos = GetViewport().GetViewport().GetMousePosition();
    _children[_currentDragControlIndex].Position = mousePos + _offset;
    var currentInsertIndex = GetInsertIndex(_children, mousePos);

    if (currentInsertIndex >= 0)
    {
      GD.Print(currentInsertIndex);
      SwapArray(_currentDragControlIndex, currentInsertIndex);
    }

    // if (currentInsertIndex != _lastInsertIndex)
    // {
    //   _lastInsertIndex = currentInsertIndex;
    //
    //   OnInsertIndexChanged(currentInsertIndex, _currentDragControl);
    // }
  }

  #endregion

  #region methods

  /// <summary>
  /// 
  /// </summary>
  /// <param name="point"></param>
  /// <returns></returns>
  private int HitTest(Vector2 point)
  { 
    for (var i = 0; i < _children.Count; i++)
    {
      var isInChildRect = IsInRect(_children[i], point);
      if (isInChildRect == true) return i;
    }
    return -1;
  }

  /// <summary>
  /// 是否在控件中
  /// </summary>
  /// <param name="control"></param>
  /// <param name="point"></param>
  /// <returns></returns>
  private bool IsInRect(Control control, Vector2 point)
  {
    var size = control.Size;
    var pos = control.Position;
    var rect = new Rect2(pos, size);
    return rect.HasPoint(point);
  }
  
  /// <summary>
  /// 获取要交换位置的对象
  /// </summary>
  /// <param name="controls">节点数组</param>
  /// <param name="mousePos">当前鼠标位置</param>
  /// <returns>数组索引 <0 时没有找到可以插入的地点</returns>
  private int GetInsertIndex(IList<Control> controls, Vector2 mousePos)
  {
    for (var i = 0; i < controls.Count; i++)
    {
      // 如果鼠标在某个control区域,返回
      if (mousePos.X > controls[i].Position.X && mousePos.X < controls[i].Position.X + controls[i].Size.X &&
          mousePos.Y > controls[i].Position.Y && mousePos.Y < controls[i].Position.Y + controls[i].Size.Y &&  // 是否在节点区域内
          _currentDragControlIndex != i && !_moveControlIndexs.Contains(i)) // 不能包括自己和正在进行动画的节点
      {
        return i;
      }
    }

    return -1;
  }

  /// <summary>
  /// 交换数组位置
  /// </summary>
  /// <param name="dragIndex">拖动的节点index</param>
  /// <param name="index">要交换的节点index</param>
  private void SwapArray(int dragIndex, int index)
  {
    // 保存节点要移动到的坐标
    var pos = _currentDragControlPos;
    
    // 记录新坐标
    _currentDragControlPos = _children[index].Position;
    // 记录新拖动index
    _currentDragControlIndex = index;
    
    // 移动交换节点的位置 因为后面交换了数组位置,这里传入的是dragIndex
    TweenMove(_children[index], pos, dragIndex);
    
    // 交换数组元素
    (_children[dragIndex], _children[index]) = (_children[index], _children[dragIndex]);
    
  }
  
  /// <summary>
  /// 平滑移动
  /// </summary>
  /// <param name="control"></param>
  /// <param name="targetPos"></param>
  /// <param name="dragIndex"></param>
  private void TweenMove(Control control, Vector2 targetPos, int dragIndex)
  {
    _moveControlIndexs.Add(dragIndex);
    var tween = control.CreateTween();
    tween.SetTrans(Tween.TransitionType.Quart);
    tween.SetEase(Tween.EaseType.Out);
    tween.TweenProperty(control,"position", targetPos, 0.2d);
    tween.Finished += () => { _moveControlIndexs.Remove(dragIndex); }; // 完成后从正在变化的数组中移除
  }
  
  ///<summary>
  /// 当前容器内容发生更改时
  /// </summary>
  private void ReloadChildren()
  {
    if (_parent == null) return;

    var childCount = _parent.GetChildCount(false);

    var result = new List<Control>();
    for (int i = 0; i < childCount; i++)
    {
      var child = _parent.GetChildOrNull<Control>(i);
      if (child == null) continue;
      result.Add(child);
    }

    _children = result;
  }

  #endregion

  #region child change

  /// <summary>
  /// 节点进入树时执行
  /// </summary>
  /// <exception cref="NotImplementedException"></exception>
  private void DragableVerticalStackPanel_TreeEntered()
  {
    // 获取父节点
    var tree = GetParent<Control>();
    // 有子节点进入树
    tree.ChildEnteredTree += Tree_ChildEnteredTree;
    // 有子节点出树
    tree.ChildExitingTree += Tree_ChildExitingTree;
    // 子节点顺序变化
    tree.ChildOrderChanged += Tree_ChildOrderChanged;

    _parent = tree;
  }

  private void Tree_ChildOrderChanged()
  {
    GD.Print("Tree_ChildOrderChanged");
    ReloadChildren();
  }

  private void Tree_ChildExitingTree(Node node)
  {
    GD.Print("Tree_ChildExitingTree", node.ToString());
    ReloadChildren();
  }

  private void Tree_ChildEnteredTree(Node node)
  {
    GD.Print("Tree_ChildEnteredTree", node.ToString());
    ReloadChildren();
  }

  #endregion
}